/*
	2024全年项目
	124.221.142.162:8081 网站的Rust语言重写,数据结构待定,候选方案是BTreeMap<Pos,*>与Vec<Vec<*>>,而"*"同样也待定。
	还需要把棋盘改成左右绕接上下绕接的结构以最终回答这个问题https://www.zhihu.com/question/637281442

*/
fn main() {
	use my::*;
	use rand::{thread_rng, Rng};
	use rand::distributions::{Distribution, Uniform};

	let (rows, cols) = (13,13);

	let mut w = World::<u32>::new(rows,cols);


	let mut rng = thread_rng();
	//let chars: Vec<_> = ('A'..='G').collect() ;
	let chars: Vec<_> = (1..=9).collect() ;
	let rand_char = Uniform::from(0..chars.len());
	loop {
		let (c, row, col)
			= (chars[rand_char.sample(&mut rng)], rng.gen_range(0..rows),  rng.gen_range(0..cols));
		w.go(c, col.try_into().unwrap(), row.try_into().unwrap());
	}


}



mod my {
	use std::collections::HashSet;
	//use std::collections::BTreeMap;
	use std::convert::From;
	use std::fmt;
	use std::marker::Copy;
	use std::cmp::{PartialEq, Eq};

	type Uid = char;
	type StoneID = u32;
	type BlockID = StoneID;

	type Board<T> =
		Vec<Vec<Option<T>>>;
		//[[T ;cols] ;rows];


	#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
	struct Hand<T: Copy + PartialEq + Eq> {
		real_pos: RealPos,
		uid: T,
	}

	#[derive(PartialEq,PartialOrd,Eq,Ord,Debug)]
	pub struct VirtualPos (pub i32, pub i32);
	impl VirtualPos  {
		fn to_real_pos(&self, limit: (&usize, &usize)) -> RealPos {
			let origin_pos: RealPos = RealPos{col:limit.0 /2, row: limit.1 /2};
			RealPos{col: get_real_index(&((origin_pos.col as i32) + self.0), limit.0 ),
					row: get_real_index(&((origin_pos.row as i32) - self.1), limit.1 )}
		}
	}

	use std::ops;
	impl ops::Add<VirtualPos> for VirtualPos {
		type Output = Self;
		fn add(self, _rhs: Self) -> Self {
			VirtualPos(self.0 + _rhs.0, self.1 + _rhs.1)
		}
	}

	#[derive(Hash, Eq, PartialEq, Debug, PartialOrd, Ord, Clone, Copy)]
	pub struct RealPos{pub row: usize, pub col: usize}

	struct Stone<'a> {
		id: StoneID,
		pos:   &'a RealPos,
		block: &'a Block,
	}

	struct Block {
		id:	BlockID,
		uid:    Uid,
		qishu:  u32,
	}

	#[derive(PartialEq)]
	pub struct World
		<T: Copy
			+ std::cmp::Eq
			+ std::hash::Hash
		 > {
		rows: usize,
		cols: usize,
		pub board: Board<T>,
		pub cycle: usize,
		last_hand: Option<Hand<T>>,
	}

	impl<T: std::clone::Clone
		 + std::fmt::Display
		 + std::fmt::Debug
		 + std::cmp::PartialEq
		 + std::cmp::Eq
		 + Copy
		 + std::hash::Hash
		 > World<T> {
		pub fn new(rows: usize, cols: usize) -> World<T> {
			World {
				rows: rows,
				cols: cols,
				board: vec![ vec![None;cols]; rows],
				cycle: 0,
				last_hand: None,
			}
		}
		pub fn go(&mut self, uid: T, x: i32, y: i32)->&mut Self {
			let hand = Hand {
				real_pos: VirtualPos(x, y).to_real_pos((&self.cols, &self.rows)),
				uid: uid,
			};

			let mut ref_v = &mut self.board[*&hand.real_pos.row][*&hand.real_pos.col];
			if *ref_v == None {
				*ref_v = Some(*&hand.uid);

				self.last_hand = Some(*&hand);
				println!("{:?} OK", hand);

				self.eat();
				self.cycle += 1;

				println!("{:?}", *self);
			} else {
				println!("{:?} duplicated!", hand.real_pos);
			}



			return self;
		}



		pub fn chi(&self, real_pos: RealPos) -> usize {
			0
		}

		pub fn get_block(&self, real_pos: RealPos) -> HashSet<RealPos>
		{
			HashSet::from([])
		}

		fn up(&self, this_pos: &RealPos) ->RealPos {
			RealPos {
				row:
					if this_pos.row == 0 {
						self.rows - 1
					} else {
						this_pos.row - 1
					},
				col: this_pos.col
			}
		}
		fn down(&self, this_pos:&RealPos) ->RealPos {
			RealPos {
				row:
					if this_pos.row == self.rows - 1 {
						0
					} else {
						this_pos.row + 1
					},
				col: this_pos.col
			}
		}
		fn left(&self, this_pos: &RealPos) ->RealPos {
			RealPos {
				row: this_pos.row,
				col:
					if this_pos.col == 0 {
						self.cols - 1
					} else {
						this_pos.col - 1
					}
			}
		}
		fn right(&self, this_pos: &RealPos) ->RealPos {
			RealPos {
				row: this_pos.row,
				col:
					if this_pos.col == self.cols - 1 {
						0
					} else {
						this_pos.col + 1
					}
			}
		}

		pub
		fn is_aliving(&self, pos: RealPos) -> (bool, Option<HashSet<RealPos>>) {
			/*
			let up_pos_ref =	&self.up   (&_pos);
			let dn_pos_ref =	&self.down (&_pos);
			let lf_pos_ref =	&self.left (&_pos);
			let rt_pos_ref =	&self.right(&_pos);
			*/

			let check = |_pos: &RealPos| {
				*self.get(            _pos ) == None ||
				*self.get(&self.up   (_pos)) == None ||
				*self.get(&self.down (_pos)) == None ||
				*self.get(&self.left (_pos)) == None ||
				*self.get(&self.right(_pos)) == None
			};
			let mut plan_to_check: Vec<RealPos> = vec![pos];
			let mut checked: HashSet<RealPos> = HashSet::new();
			loop {
				if plan_to_check.is_empty() {
					return (false, Some(checked));
				}

				let _pos = plan_to_check.pop().unwrap();
				if checked.contains(&_pos) {
					continue;
				}
				if check(&_pos) {
					return (true, None);
				} else {
					checked.insert(_pos.clone());
					let ref_value = self.get(&_pos);
					if *self.get(&self.up(&_pos)) == *ref_value {
						plan_to_check.push(self.up(&_pos));
					}
					if *self.get(&self.down (&_pos)) == *ref_value {
						plan_to_check.push(self.down (&_pos));
					}
					if *self.get(&self.left (&_pos)) == *ref_value {
						plan_to_check.push(self.left (&_pos));
					}
					if *self.get(&self.right(&_pos)) == *ref_value {
						plan_to_check.push(self.right(&_pos));
					}
				}
			}

		}

		fn eat<'a>(&mut self) /*-> Vec<(RealPos, T)>*/{
			let mut enemy: HashSet<RealPos> = HashSet::new();
			if let Some(this_hand) = &self.last_hand {

				if self.get(&self.up(&this_hand.real_pos)) != &Some(this_hand.uid) {
					if let (false, Some(u)) = self.is_aliving(self.up(&this_hand.real_pos)) {
						enemy = &enemy | &u;
						//println!("U{:#?}", &u);
					}
				}
				if self.get(&self.down(&this_hand.real_pos)) != &Some(this_hand.uid) {
					if let (false, Some(u)) = self.is_aliving(self.down(&this_hand.real_pos)) {
						enemy = &enemy | &u;
						//println!("D{:#?}", &u);
					}
				}
				if self.get(&self.left(&this_hand.real_pos)) != &Some(this_hand.uid) {
					if let (false, Some(u)) = self.is_aliving(self.left(&this_hand.real_pos)) {
						enemy = &enemy | &u;
						//println!("L{:#?}", &u);
					}
				}
				if self.get(&self.right(&this_hand.real_pos)) != &Some(this_hand.uid) {
					if let (false, Some(u)) = self.is_aliving(self.right(&this_hand.real_pos)) {
						enemy = &enemy | &u;
						//println!("R{:#?}", &u);
					}
				}
				let print_will_removes =
					|u: &HashSet<_>| {
						println!("will remove {}: {:#?}",
								 u.len(),
								 u.iter().map(
									|&_pos|{
										Hand {
											real_pos: _pos,
											uid: self.get(&_pos)
										}
									}
									)
									.collect::<HashSet<_>>()
						);
					};
				if !enemy.is_empty() {
					print_will_removes(&enemy);
					for _pos in enemy.iter() {
						self.board[_pos.row][_pos.col] = None;
					}
					return;
				}
				if let (false, Some(u)) = self.is_aliving(this_hand.real_pos) {
					print_will_removes(&u);
					for _pos in u.iter() {
						self.board[_pos.row][_pos.col] = None;
					}
				}
			}
			//let mut result: Vec<(RealPos, T)> = vec![];
			//return result;
		}

		fn get(&self, real_pos: &RealPos) -> &Option<T> {
			&(self.board[real_pos.row][real_pos.col])
		}
	}
	impl<T: Copy + std::cmp::Eq
			+ std::hash::Hash
		 >
		From<Board<T>> for World<T> {
		fn from(arr: Board<T>) -> Self {
			World {
				rows: arr.len(),
				cols: arr[0].len(),
				board: arr,
				cycle: 0,
				last_hand: None,
			}
		}
	}
	impl From<&str> for World<char> {
		fn from(s: &str) -> Self {
			let board = str_to_broad(s);

			World {
				rows: board.len(),
				cols: board[0].len(),
				board: board,
				cycle: 0,
				last_hand: None,
			}
		}
	}
	impl<T: std::fmt::Display + Copy  + std::cmp::Eq
			+ std::hash::Hash
		 >
		fmt::Display for World<T> {
		fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
			writeln!(f, "{}x{}", self.cols,self.rows)?;
			writeln!(f, "cycle: {}", self.cycle)?;
			for row in &self.board {
				for e in row {
					match e {
						Some(c) => {write!(f, "{}",c)?;},
						_       => {write!(f, "_")?;}
					}
				}
				writeln!(f)?;
			}
			Ok(())
		}
	}
	impl<T: std::fmt::Display + Copy + std::cmp::Eq
			+ std::hash::Hash>
		fmt::Debug for World<T> {
		fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
			write!(f, "{}", self)
		}
	}

	fn get_real_index(x: &i32, _mod: &usize) -> usize {
		let m: i32 = _mod.clone().try_into().unwrap();
		if *x >= 0 {
			(*x % m) as usize
		} else {
			(m - (x.abs() % m)) as usize
		}
	}

	fn str_to_broad(s: &str) -> Board<char> {
		let mut board: Board<char> = vec![];
		for line in s.lines() {
			let mut _line = vec![];
			for c in line.chars() {
				_line.push(
				match c {
					'_' => None,
					_   => Some(c),
				});
			}
			board.push(_line);
		}
		return board;
	}

}


#[cfg(test)]
mod tests;
